package edu.chl.grupp11.aegas.controller.imp;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;

import org.apache.commons.io.FileUtils;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
import org.apache.sanselan.formats.tiff.TiffField;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.constants.TagInfo;
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
import org.farng.mp3.TagException;

import edu.chl.grupp11.aegas.constants.FileTypesConst;
import edu.chl.grupp11.aegas.controller.AbstractTagHandler;
import edu.chl.grupp11.aegas.controller.ErrorControllerFactory;
import edu.chl.grupp11.aegas.controller.IErrorController;
import edu.chl.grupp11.aegas.model.AbstractConfig;
import edu.chl.grupp11.aegas.model.AbstractMetaTag;
import edu.chl.grupp11.aegas.model.ConfigFactory;
import edu.chl.grupp11.aegas.model.imp.ImmutableMetaTag;
import edu.chl.grupp11.aegas.model.imp.MutableJpgMetaTag;
import edu.chl.grupp11.aegas.view.TagView;

public class ImageHandler implements AbstractTagHandler {
	private static final long serialVersionUID = -8079774421526943745L;
	private AbstractConfig config = ConfigFactory.getSharedConfig();
	private IErrorController errorController = ErrorControllerFactory
			.getErrorController();

	@Override
	public boolean accepts(File f) {
		try {
			extractTags(f);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	@Override
	public void editTag(File file, AbstractMetaTag iMetaTag, String newValue) {
		MutableJpgMetaTag tag = (MutableJpgMetaTag) iMetaTag;
		File dst = null;
		IImageMetadata metadata = null;
		JpegImageMetadata jpegMetadata = null;
		TiffImageMetadata exif = null;
		OutputStream os = null;
		TiffOutputSet outputSet = new TiffOutputSet();

		try {
			metadata = Sanselan.getMetadata(file);
		} catch (ImageReadException e1) {
			errorController.setErrorMessage(e1, config.getResourceBundle()
					.getString("error9"));
		} catch (IOException e1) {
			errorController.setErrorMessage(e1, config.getResourceBundle()
					.getString("error9"));
		}

		if (metadata != null) {
			jpegMetadata = (JpegImageMetadata) metadata;
		}

		if (jpegMetadata != null) {
			exif = jpegMetadata.getExif();
		}

		if (exif != null) {
			try {
				outputSet = exif.getOutputSet();
			} catch (ImageWriteException e) {
				errorController.setErrorMessage(e, config.getResourceBundle()
						.getString("error10"));
			}
		}

		if (outputSet != null) {
			TiffOutputField tiffFieldPre = outputSet
					.findField(tag.getField().tagInfo);
			if (tiffFieldPre != null) {
				outputSet.removeField(tag.getField().tagInfo);
			}
		}

		boolean failure = true;
		File tempFile = null;
		TiffOutputDirectory exifDirectory;

		try {
			if (outputSet != null) {
				exifDirectory = outputSet.getOrCreateExifDirectory();
				TiffOutputField tiffOutputField = tag
						.createTiffOutputField(newValue);
				if (tiffOutputField == null) {
					errorController.setErrorMessage(null, config
							.getResourceBundle().getString("error11"));
				}
				exifDirectory.add(tiffOutputField);

				dst = File.createTempFile("temp-" + System.currentTimeMillis(),
						".jpg");
				os = new FileOutputStream(dst);
				os = new BufferedOutputStream(os);

				tempFile = File.createTempFile(
						"temp-" + System.currentTimeMillis() + 10, ".jpg");

				FileUtils.copyFile(file, tempFile);
				new ExifRewriter().updateExifMetadataLossless(file, os,
						outputSet);
				failure = false;
			}
		} catch (ImageReadException e) {
			failure = true;
		} catch (ImageWriteException e) {
			failure = true;
		} catch (IOException e) {
			failure = true;
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					errorController.setErrorMessage(e, config
							.getResourceBundle().getString("error9"));
				}
			}
		}
		if (failure) {
			try {
				FileUtils.copyFile(tempFile, file);
			} catch (IOException e) {
				errorController.setErrorMessage(e, config.getResourceBundle()
						.getString("error6"));
			}
			iMetaTag.setValue(newValue);
		} else {
			try {
				FileUtils.copyFile(dst, file);
			} catch (IOException e) {
				errorController.setErrorMessage(e, config.getResourceBundle()
						.getString("error9"));
			}
		}
	}

	@Override
	public ArrayList<AbstractMetaTag> extractTags(File file)
			throws IOException, TagException, ImageReadException {
		ImageInfo info = org.apache.sanselan.Sanselan.getImageInfo(file);
		ArrayList<AbstractMetaTag> tags = new ArrayList<AbstractMetaTag>();

		tags.add(new ImmutableMetaTag("Format name", info.getFormatName()));
		tags.add(new ImmutableMetaTag("Height", info.getHeight() + ""));
		tags.add(new ImmutableMetaTag("Width", info.getWidth() + ""));
		tags.add(new ImmutableMetaTag("Bits per pixel", info.getBitsPerPixel()
				+ ""));

		if (hasExif(file)) {

			IImageMetadata iimageMetadata = org.apache.sanselan.Sanselan
					.getMetadata(file);

			if (iimageMetadata instanceof JpegImageMetadata) {
				JpegImageMetadata jpgImageMetadata = (JpegImageMetadata) iimageMetadata;
				TiffImageMetadata getExif = jpgImageMetadata.getExif();
				TiffOutputSet getOutputSet;
				try {
					if (getExif != null) {
						getOutputSet = getExif.getOutputSet();
						if (getOutputSet != null) {
							TiffOutputDirectory getExifDirectory = getOutputSet
									.getExifDirectory();
							if (getExifDirectory != null) {

								ArrayList<TiffOutputField> getFields = new ArrayList<TiffOutputField>();
								for (Object o : getExifDirectory.getFields()) {
									if (o instanceof TiffOutputField) {
										TiffOutputField t = (TiffOutputField) o;
										getFields.add(t);
									}
								}

								for (TiffOutputField field : getFields) {
									TagInfo tagInfo = field.tagInfo;

									TiffField dataField = getExif
											.findField(tagInfo);
									if (dataField != null) {
										tags.add(new MutableJpgMetaTag(
												dataField));
									}
								}
							}
						}
					}
				} catch (ImageWriteException e) {
					errorController.setErrorMessage(e, config
							.getResourceBundle().getString("error10"));
				}
			}
		}
		return tags;
	}

	@Override
	public ArrayList<TagView> getTagViews(File file) {
		ArrayList<TagView> tagViews = new ArrayList<TagView>();
		boolean fail = false;

		try {
			for (AbstractMetaTag tag : extractTags(file)) {
				tagViews.add(new TagView(tag));
			}
		} catch (UnsupportedOperationException e) {
			fail = true;
		} catch (IOException e) {
			fail = true;
		} catch (TagException e) {
			fail = true;
		} catch (ImageReadException e) {
			fail = true;
		}

		if (fail) {
			errorController.setErrorMessage(null, config.getResourceBundle()
					.getString("error6"));
			fail = false;
		}
		return tagViews;
	}

	private boolean hasExif(File file) {
		if (FileTypesConst.IMAGE_FILE_FILTER.accept(file)) {
			try {
				return org.apache.sanselan.Sanselan.getMetadata(file) != null;
			} catch (ImageReadException e) {
			} catch (IOException e) {
			}
		}
		errorController.setErrorMessage(null, config.getResourceBundle()
				.getString("error9"));
		return false;
	}
}
